﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ConsoleUtility.Command
{
    /// <summary>
    /// 解析失败事件参数
    /// </summary>
    public class ParseFailedEventArgs : EventArgs
    {
        public string Input { get; set; }

        public ParseFailedEventArgs(string input)
        {
            Input = input;
        }
    }

    /// <summary>
    /// 命令帮助类
    /// </summary>
    public static class CommandHelper
    {
        //命令列表
        private static List<Command> commands = new List<Command>();

        /// <summary>
        /// 解析失败
        /// </summary>
        public static event EventHandler<ParseFailedEventArgs> ParseFailed;

        /// <summary>
        /// 命令列表
        /// </summary>
        public static List<Command> Commands { get { return commands; } }

        /// <summary>
        /// 执行命令
        /// </summary>
        /// <param name="input">输入</param>
        /// <returns></returns>
        public static object ExcuteCommand(string input)
        {
            var excuter = new CommandExcuter();
            if (!excuter.Parse(input))
            {
                ParseFailed?.Invoke(null, new ParseFailedEventArgs(input));
                return null;
            }

            return excuter.Excute();
        }

        /// <summary>
        /// 设置命令
        /// </summary>
        /// <param name="sourceType">资源类型</param>
        public static void SetCommand(Type sourceType)
        {
            if (sourceType == null)
                return;

            var commandAtt = sourceType.GetCustomAttribute<CommandAttribute>();
            if (commandAtt == null)
            {
                throw new Exception("CommandAttribute required.");
            }

            var command = new Command
            {
                SourceType = sourceType,
                Name = commandAtt.Name
            };

            SetMethods(sourceType, command);
            SetOptions(sourceType, command);

            command.DefaultMethod = command
                .Methods
                .Select(m => m.Info)
                .FirstOrDefault(i => i?.Name == commandAtt.DefaultMehtod);

            commands.Add(command);
        }

        /// <summary>
        /// 设置方法
        /// </summary>
        /// <param name="sourceType">资源类型</param>
        /// <param name="command">命令</param>
        private static void SetMethods(Type sourceType, Command command)
        {
            var methodInfos = sourceType.GetMethods();
            if (methodInfos == null || methodInfos.Length == 0)
            {
                throw new Exception("Method required.");
            }

            foreach (var info in methodInfos)
            {
                var methodAtt = info.GetCustomAttribute<MethodAttribute>();
                if (methodAtt == null)
                    continue;

                if (command.Methods.Any(m => m.Name == methodAtt.Name))
                { throw new Exception("Method name existed."); }

                command.Methods.Add(new Method
                {
                    Info = info,
                    Name = methodAtt.Name,
                    Character = methodAtt.Character
                });
            }

            if (command.Methods.Count == 0)
            {
                throw new Exception("MethodAttribute required.");
            }
        }

        /// <summary>
        /// 设置选项
        /// </summary>
        /// <param name="sourceType">资源类型</param>
        /// <param name="command">命令</param>
        private static void SetOptions(Type sourceType, Command command)
        {
            var propertyInfos = sourceType.GetProperties();
            if (propertyInfos == null || propertyInfos.Length == 0)
                return;

            foreach (var info in propertyInfos)
            {
                if (info.PropertyType != typeof(bool) && info.PropertyType != typeof(string))
                    continue;

                var optionAtt = info.GetCustomAttribute<OptionAttribute>();
                if (optionAtt == null)
                    continue;

                if (command.Options.Any(o => o.Name == optionAtt.Name))
                { throw new Exception("Option name existed."); }

                command.Options.Add(new Option
                {
                    Info = info,
                    Name = optionAtt.Name,
                    Character = optionAtt.Character
                });
            }
        }

        /// <summary>
        /// 设置命令
        /// </summary>
        /// <param name="sourceTypes">资源类型</param>
        public static void SetCommands(IEnumerable<Type> sourceTypes)
        {
            if (sourceTypes == null || sourceTypes.Count() == 0)
                return;

            foreach (var sourceType in sourceTypes)
            { SetCommand(sourceType); }
        }

        /// <summary>
        /// 设置命令
        /// </summary>
        /// <param name="command">命令</param>
        public static void SetCommand(Command command)
        {
            if (command == null)
                return;

            commands.Add(command);
        }

        /// <summary>
        /// 设置命令
        /// </summary>
        /// <param name="commands">命令</param>
        public static void SetCommands(IEnumerable<Command> commands)
        {
            if (commands == null || commands.Count() == 0)
                return;

            foreach (var command in commands)
            { SetCommand(command); }
        }
    }
}
